import java.util.ArrayList;

//this class "simulates" the os.  it is created by the main ScheduleSimultor class and initliazed with processes to run
//and a type of scheduling system to use.  you call run on it and it uses the scheduling algorithm to run schedule the process
//to run

//once the system is done running, you can call print results on it to display statistical data

public class OSSimulator {
	
	long clk;
	int choice = -1;
	
	FCFS algthmFCFS;
	MLFQ algthmMLFQ;
	RoundRobin algthmRR;
	Process emptyCycle = null; 		//Used to indicate idle cycle
	ArrayList<clockCycle> clkSummary;
	ArrayList<Process> proc;
	
	//need to create several queues up here to store processes
	//initialized queue
	
	public OSSimulator(String schedAlg, ArrayList<Process> initProc)
	{
		clk = 0;
		proc = initProc;
		clkSummary = new ArrayList<clockCycle>();
		
		if(schedAlg.equals("fcfs")) {
			choice = 0;
			algthmFCFS = new FCFS();
		}
		else if(schedAlg.equals("rr1")) {
			choice = 1;
			algthmRR = new RoundRobin(10);
		}
		else if(schedAlg.equals("rr2")) {
			choice = 2;
			algthmRR = new RoundRobin(50);
		}
		else if(schedAlg.equals("mlfq")) {
			choice = 3;
			algthmMLFQ = new MLFQ();
		}
	}

	public void Run()
	{
		clk = 0;
		
		while(simFinished()) {
			
			for(int k=0; k < proc.size(); k++) {

			switch (choice) {
			case 0:
				System.out.println("You are running the FCFS Algorithm");
				algthmFCFS.RunAReadyProcessForTick();
				algthmFCFS.readyQueueRef.add(proc.get(k));
				break;
			case 1:
				System.out.println("You are running the Round Robin Algorithm with time quantum 10");
				algthmRR.RunAReadyProcessForTick();
				algthmRR.readyQueueRef.add(proc.get(k));
				break;
			case 2:
				System.out.println("You are runnin the Round Robin Algorithm with time quantum 50");
				break;
			case 3:
				System.out.println("You are running the MLFQ Algorithm");
				break;
			default:
				System.out.println("INVALID Algorithm Choice");
				break;
			}
			
			}
		
		//run for 1 tick!
		
		//move processes through the queues where they should be
		//get the process to run from the feedback queue
		//run that process for one tick
		//(when the process runs for one tick it should update its internals so that it knows it was run)
		
		if(algthmFCFS.idleProc())
			clkSummary.add(new clockCycle(clk, emptyCycle));
		else
			clkSummary.add(new clockCycle(clk, proc.get(0) ));
		
		clk++;
		
		}
	}
	
	public boolean simFinished() {
		
		for(int j=0; j < proc.size(); j++) {
			if(proc.get(j).IsFinished())
				return false;
		}
		return true;
		
	}

	public void PrintStatistics()
	{
		//should we only print out the end statistics? what data do we want to print out
		//StatsOnlyFlag to decide whether to print out algorithm decisions or statistics only
		if (SimScheduler.statsOnlyFlag)
		{
			for(int i=0; i < clkSummary.size(); i++) {
				
				clockCycle c = clkSummary.get(i);
				
				if(c.clockProc == null) {
					System.out.print("(" + c.getCycle() + ",idle)  ");
				}
				else {
					System.out.print("(" + c.getCycle() + "," + c.getPid() + "," + c.getPname() + ")  ");
				}
			}
			
		}
		
		//go through process list and print out general results
		
		//print out statistics for the algorithm used
		
	}
	
	public class clockCycle {
		
		long startTime;			//Process start time and process running at this time
		Process clockProc;
		
		public clockCycle (long startofProc, Process clockProcess) {
			startTime = startofProc;
			clockProc = clockProcess;
		}
		
		public long getCycle() {
			return startTime;
		}
		
		public int getPid() {
			return clockProc.processId;
		}
		
		public String getPname() {
			return clockProc.processName;
		}	
	}
	
}
